/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package moteur;

import gui.Grille;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Observable;
import java.util.logging.Level;
import java.util.logging.Logger;
import jreversi.Coordonnee;
import player.*;
import serveur.ReseauLan;

/**
 *
 * @author Jean
 */
public class Plateau extends Observable implements Serializable
{

    private char[][] tabPlateau =
    {
        {
            'e', 'e', 'e', 'e', 'e', 'e', 'e', 'e'
        },
        {
            'e', 'e', 'e', 'e', 'e', 'e', 'e', 'e'
        },
        {
            'e', 'e', 'e', 'e', 'e', 'e', 'e', 'e'
        },
        {
            'e', 'e', 'e', 'n', 'b', 'e', 'e', 'e'
        },
        {
            'e', 'e', 'e', 'b', 'n', 'e', 'e', 'e'
        },
        {
            'e', 'e', 'e', 'e', 'e', 'e', 'e', 'e'
        },
        {
            'e', 'e', 'e', 'e', 'e', 'e', 'e', 'e'
        },
        {
            'e', 'e', 'e', 'e', 'e', 'e', 'e', 'e'
        }
    };
    char[][] savePlateau = new char[8][8];
    ArrayList<Coordonnee> casesAFlip = new ArrayList<Coordonnee>();
    public char tour = 'n'; // les noirs commence toujours
    private int lastXClick = -1; // Pour Savoir ou pour la derniere fois nous avons clicker ( dernier cojup jouer )
    private int lastYClick = -1; // Pour Savoir ou pour la derniere fois nous avons clicker ( dernier cojup jouer )
    private int scoreN = 0;
    private int scoreB = 0;
    Joueur J1 = new Human();
    Joueur J2 = new Computer();
    /** L'instance statique */
    private static Plateau instance;

    /** Récupère l'instance unique de la class Singleton.<p>
     * Remarque : le constructeur est rendu inaccessible
     */
    public static Plateau getInstance()
    {
        if (instance == null)
        { // Premier appel
            instance = new Plateau();
        }
        return instance;
    }

    /** Constructeur redéfini comme étant privé pour interdire
     * son appel et forcer à passer par la méthode getInstance
     */
    private Plateau()
    {
    }

    /**
     * Permet de recuperer le plateau COTE MOTEUR
     */
    public char[][] getTabPlateau()
    {
        return tabPlateau;
    }

    /**
     * Actionne les joueurs et les positionnes en attente ou les autorisent a jouer
     */
    public void jouerTour()
    {
        if (tour == 'n')
        {
            J1.jouer();
            if (J1.toChar() == 'N' || J2.toChar() == 'N')
            {
                ReseauLan.getInstance().send();
            }
            J2.attendre();
        }
        else
        {
            J1.attendre();
            if (Plateau.getInstance().getJ1().toChar() == 'N' || Plateau.getInstance().getJ2().toChar() == 'N')
            {
                ReseauLan.getInstance().send();
            }

            J2.jouer();
        }
    }

    /**
     *Change le tour
     */
    public void changeTour()
    {
        if (tour == 'b')
        {
            tour = 'n';
        }
        else
        {
            tour = 'b';
        }
    }

    /**
     * Permet de poser un pion sur le plateau COTE MOTEUR
     * @param   x   Coordonnées X du pion dans le tableau <i>(Max : 8)</i>.
     * @param   y   Coordonnées Y du pion dans le tableau <i>(Max : 8)</i>.
     */
    public void posePion(int x, int y, ArrayList<Coordonnee> aRetourner)
    {
        boolean partiFini = false;

        if (tabPlateau[x][y] == 'e')
        {
            casesAFlip = aRetourner;
            tabPlateau[x][y] = tour;
            retournerPion();
            changeTour();

            setLastXClick(x);
            setLastYClick(y);

            setChanged();
            notifyObservers();
            partiFini = score();
            if (partiFini)
            {
                Grille.getInstance().partiFini(scoreN, scoreB);
            }
        }
    }

    /**
     * Permet de poser un pion sur le plateau sans que L IHM soit averti
     * @param   x   Coordonnées X du pion dans le tableau <i>(Max : 8)</i>.
     * @param   y   Coordonnées Y du pion dans le tableau <i>(Max : 8)</i>.
     */
    public void poseFakePion(int x, int y, ArrayList<Coordonnee> aRetourner)
    {
        if (tabPlateau[x][y] == 'e')
        {
            casesAFlip = aRetourner;
            tabPlateau[x][y] = tour;
            retournerPion();
            changeTour();
        }
    }

    /**
     * Retourne les pions qui sont a retourné
     * Fontions appelé par posePion
     */
    private void retournerPion()
    {
        int x = 0, y = 0;
        for (int i = 0; i < casesAFlip.size(); i++)
        {
            x = casesAFlip.get(i).getX();
            y = casesAFlip.get(i).getY();
            tabPlateau[x][y] = tour;
        }
    }

    /**
     *Clone du Plateau
     */
    @Override
    protected Plateau clone() throws CloneNotSupportedException
    {
        Plateau plat = new Plateau();
        plat.tabPlateau = copyTabPlateau();
        plat.tour = this.tour;
        return plat;
    }

    /**
     *Fontion de debug pour regarder a quoi ressemble le tableau coté moteur
     */
    public void toStringTab()
    {
        for (int i = 0; i < tabPlateau.length; i++)
        {
            for (int j = 0; j < tabPlateau[0].length; j++)
            {
                System.out.print(tabPlateau[i][j] + ", ");
            }
            System.out.println("");
        }
    }

    /**Si c'est le tour de l'humain*/
    public boolean isHumanPlayerTurn()
    {
        Joueur j = null;
        boolean rez = false;

        if (tour == 'n')
        {
            j = J1;
        }
        else
        {
            j = J2;
        }

        if (j.toChar() == 'H')
        {
            rez = true;
        }

        return rez;
    }

    /**
    A qui est donc cette couleur ?
     */
    public char aQui(char couleur)
    {
        char rez = 'E';
        if (couleur != 'e')
        {
            if (couleur == 'n')
            {
                rez = J1.toChar();
            }
            else
            {
                rez = J2.toChar();
            }
        }

        return rez;

    }

    /*Sauvegarde la table de jeu en cours afin de pouvoir modifier
     * la table actuelle (simulation de coup etc...)
     */
    public void saveTab()
    {
        for (int i = 0; i < tabPlateau.length; i++)
        {
            for (int j = 0; j < tabPlateau.length; j++)
            {
                savePlateau[i][j] = tabPlateau[i][j];
            }
        }
    }

    /*Restaure la copie du tableau qu'on a fait avec <i>saveTab()</i>*/
    public void restoreTab()
    {
        tabPlateau = savePlateau;
        savePlateau = new char[8][8];
    }

    /*Reset l'instance afin de faire une nouvelle partie*/
    public static void reset()
    {
        instance = null;
    }

    private char[][] copyTabPlateau()
    {
        char[][] tempPlateau = new char[8][8];
        for (int i = 0; i < tabPlateau.length; i++)
        {
            for (int j = 0; j < tabPlateau.length; j++)
            {
                tempPlateau[i][j] = tabPlateau[i][j];
            }
        }
        return tempPlateau;
    }

    public int getLastXClick()
    {
        return lastXClick;
    }

    public void setLastXClick(int lastXClick)
    {
        this.lastXClick = lastXClick;
    }

    public int getLastYClick()
    {
        return lastYClick;
    }

    public void setLastYClick(int lastYClick)
    {
        this.lastYClick = lastYClick;
    }

    /* CALCUL DU SCORE A AFFICHER ET RETOURNE TRUE SI TOUTES LES CASES SONT REMPLIS (JEU FINI)*/
    public boolean score()
    {
        scoreB = 0;
        scoreN = 0;
        int cpt = 0;
        boolean res = true;
        for (int i = 0; i < tabPlateau.length; i++)
        {
            for (int j = 0; j < tabPlateau[0].length; j++)
            {
                if (tabPlateau[i][j] == 'b')
                {
                    scoreB++;
                }
                else if (tabPlateau[i][j] == 'n')
                {
                    scoreN++;
                }
                else
                {
                    cpt++;
                }
            }
        }
        if (cpt > 0)
        {
            res = false;
        }
        return res;

    }

    public int getScoreB()
    {
        return scoreB;
    }

    public int getScoreN()
    {
        return scoreN;
    }

    public Joueur getJ1()
    {
        return J1;
    }

    public void setJ1(Joueur J1)
    {
        this.J1 = J1;
    }

    public Joueur getJ2()
    {
        return J2;
    }

    public void setJ2(Joueur J2)
    {
        this.J2 = J2;
    }

    /**
    Configure les joueurs lors d'une nouvelle partie
     * 
     * @param   c   lettre pouvant prendre la valeur c (Human vs Computer) ou h(Human vs Human).
     * @param   difficulte   niveau choisi pour l'ordinateur.
     * @param   coul   lettre pouvant prendre la valeur b ou n. Designant la couleur du Joueur.
     */
    public void paramNewPlat(char c, int difficulte, char coul)
    {
        if (c == 'c')
        {
            if (coul == 'b')
            {
                J1 = new Computer(difficulte);
                J2 = new Human();
            }
            else
            {
                J1 = new Human();
                J2 = new Computer(difficulte);
            }

        }
        else if (c == 'h')
        {
            J1 = new Human();
            J2 = new Human();
        }

        jouerTour();
    }

    public void save()
    {
        FileOutputStream file = null;
        {
            ObjectOutputStream os = null;
            try
            {
                file = new FileOutputStream("lastPartie.dat");
                os = new ObjectOutputStream(file);
                J1.attendre();
                J2.attendre();
                os.writeObject(this);
                os.flush();

            }
            catch (IOException ex)
            {
                Logger.getLogger(Plateau.class.getName()).log(Level.SEVERE, null, ex);
            }
            finally
            {
                try
                {
                    file.close();
                }
                catch (IOException ex)
                {
                    Logger.getLogger(Plateau.class.getName()).log(Level.SEVERE, null, ex);
                }
                try
                {
                    os.close();
                }
                catch (IOException ex)
                {
                    Logger.getLogger(Plateau.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
        }

    }

    public boolean isSaveFile()
    {
        boolean exist = true;
        FileInputStream file = null;
        try
        {
            file = new FileInputStream("lastPartie.dat");
            file.close();
        }
        catch (IOException ex)
        {
            exist = false;
        }
        finally
        {
            return exist;
        }
    }

    public void load()
    {
        FileInputStream file = null;
        ObjectInputStream os = null;
        try
        {

            File f = new File("lastPartie.dat");
            file = new FileInputStream("lastPartie.dat");
            os = new ObjectInputStream(file);
            Plateau platTemp = (Plateau) os.readObject();

            instance.J1 = platTemp.J1;
            instance.J2 = platTemp.J2;
            instance.tabPlateau = platTemp.tabPlateau;
            instance.tour = platTemp.tour;
            instance.lastXClick = platTemp.lastXClick;
            instance.lastYClick = platTemp.lastYClick;
            
            os.close();
            file.close();
            f.delete();
            
            setChanged();
            notifyObservers();
        }
        catch (ClassNotFoundException ex)
        {
            Logger.getLogger(Plateau.class.getName()).log(Level.SEVERE, null, ex);
        }
        catch (IOException ex)
        {
            Logger.getLogger(Plateau.class.getName()).log(Level.SEVERE, null, ex);
        }
        finally
        {
        }
    }
}
